package com.godme.structure;

import java.awt.*;

public class Hash<K, V> {

    private Node<Entry<K,V>>[] container;
    private int containerSize;
    private int dataCount;


    public Hash(){
        this(5);
    }

    public Hash(int containerSize){
        container = new Node[containerSize];
        this.containerSize = containerSize;
    }

    public boolean overThreshold(){
        return (dataCount + 0.1) / containerSize > 0.75;
    }

    public void resize(){
        int newContainerSize = 2 * containerSize;
        Node<Entry<K, V>>[] newContainer = new Node[newContainerSize];
        for(int index = 0; index < containerSize; index++){
            Node<Entry<K, V>> entryNode = container[index];
            while (entryNode != null){
                doPut(newContainer, entryNode.v.k, entryNode.v.v);
                entryNode = entryNode.next;
            }
        }
        container = newContainer;
        containerSize = newContainerSize;
    }

    public void put(K k, V v){
        if(overThreshold()){
            resize();
        }
        doPut(container, k, v);
        dataCount += 1;
    }

    public V get(K k){
        int hashCode = k.hashCode();
        Node<Entry<K, V>> entryNode = container[formatHash(k, containerSize)];
        while (entryNode!=null){
            if (entryNode.v.hashCode == hashCode){
                return entryNode.v.v;
            }
        }
        return null;
    }

    private void doPut(Node<Entry<K, V>>[] c, K k, V v){
        int index = formatHash(k, c.length);
        Node<Entry<K,V>> target = c[index];
        if(target == null){
            c[index] = new Node<Entry<K, V>>(new Entry<K, V>(k, v));
        } else {
            int currentHashCode = k.hashCode();
            while(true){
                if(target.v.hashCode == currentHashCode){
                    target.v.v = v;
                    return;
                }
                if(target.next == null){
                    Entry<K, V> newEntry = new Entry<K, V>(k, v);
                    target.next = new Node<Entry<K, V>>(newEntry);
                    return;
                }
                target = target.next;
            }
        }
    }


    private int formatHash(Object key, int length){
        int hashCode = key.hashCode();
        return Math.abs(hashCode) % length;
    }

    public void show(){
        StringBuilder sb = new StringBuilder("{\n");
        for(int index=0; index < containerSize; index++){
            Node<Entry<K, V>> entryNode = container[index];
            while(entryNode !=null){
                sb.append("\t").append(entryNode.v.k).append(":").append(entryNode.v.v).append("\n");
                entryNode = entryNode.next;
            }
        }
        System.err.println(sb.append("}"));
    }


    static class Entry<K, V>{
        K k;
        V v;
        int hashCode;

        public Entry(K k, V v){
            this.k = k;
            this.v = v;
            this.hashCode = k.hashCode();
        }
    }

    static class Node<V> {
        V v;
        Node<V> next;

        public Node(V v){
            this.v = v;
        }
    }


    public static void main(String[] args) {
        Hash<String, String> hash = new Hash<String, String>();
        for (int i=0; i<20; i++){
            hash.put("key"+i, "value"+i);
        }

        hash.show();

        for (int i=0; i<20; i++){
            System.err.println(String.format("get\tkey:%s\tvalue:%s", "key"+i, hash.get("key"+i)));
        }
    }




}
